#include "cardset.h"

using Cards = QVector<Card>;

template <typename It>
static inline bool allEqual(It first, It last)
{
    if (first == last) return false; // for safety
    auto c = *first++;
    for (It it = first; it != last; ++it)
        if (*it != c)
            return false;
    return true;
}

template <typename Container>
static inline bool allEqual(const Container& cont)
{
    return allEqual(cont.begin(), cont.end());
}

// 三带一
static inline bool isTripletWithOne(const Cards& v)
{
    auto a = v.begin();
    return allEqual(a, a + 3) || allEqual(a + 1, a + 4);
}

// 三带二
static inline bool isTripletWithPair(const Cards& v)
{
    auto a = v.begin();
    return (allEqual(a, a + 3) && v[3] == v[4]) ||
           (v[0] == v[1] && allEqual(a + 2, a + 4));
}

// 顺子
static inline bool isContinuousSingle(const Cards& v)
{
    auto n = v.size();
    if (n < 5 || n > 12)
        return false;
    for (int i = 0; i < n - 1; ++i) {
        if (v[i] >= 15) // 2, 小王, 大王
            return false;
        if (v[i] != v[i + 1] + 1)
            return false;
    }
    return true;
}

// 连对
static inline bool isContinuousPair(const Cards& v)
{
    auto n = v.size();
    if (n < 6 || n % 2 != 0)
        return false;
    for (int i = 0; i < n; i += 2) {
        if (v[i] >= 15) // 2
            return false;
        if (v[i] != v[i + 1])
            return false;
        if (i < n - 2)
            if (v[i] != v[i + 2] + 1)
                return false;
    }
    return true;
}

// 四带二(对)
static inline bool isQuadWithPair(const Cards& v)
{
    auto a = v.begin();
    if (v.size() == 6)
        return allEqual(a, a + 4) || allEqual(a + 1, a + 5) || allEqual(a + 2, a + 6);
    else if (v.size() == 8) {
        return (allEqual(a, a + 4) && v[4] == v[5] && v[6] == v[7]) ||
               (v[0] == v[1] && allEqual(a + 2, a + 6) && v[6] == v[7]) ||
               (v[0] == v[1] && v[2] == v[3] && allEqual(a + 4, a + 8));
    }
    return false;
}

/**
 * @brief isAirplane
 * @note only these types of 飞机 are accepted:
 * 1. 333 444 (6)
 * 2. 333 444 56 (8)
 */
static inline bool isAirplane(const Cards& v)
{
    if (v.size() == 6) {
        auto a = v.begin();
        return v[0] == v[3] + 1 && allEqual(a, a + 3) && allEqual(a + 3, a + 6);
    } else if (v.size() == 8) {
        int cnt[18];
        for (int i = 3; i <= 17; ++i)
            cnt[i] = 0;
        for (auto c : v)
            cnt[c]++;

        int x3[2], n = 0;
        for (int i = 3; i <= 17; ++i)
            if (cnt[i] >= 3)
                x3[n++] = i;
        if (n != 2)
            return false;
        if (x3[0] + 1 != x3[1])
            return false;
        return true;
    }
    return false;
}

// get value for "三带" and "四带二" (n = 3 / 4)
static inline int getValue(const Cards& v, int n)
{
    int cnt[18];
    for (int i = 3; i <= 17; ++i)
        cnt[i] = 0;
    for (Card c : v)
        cnt[c]++;

    for (int i = 17; i >= 3; --i)
        if (cnt[i] >= n)
            return i;
    __builtin_unreachable();
}

QString CardSet::getType() const
{
    if (cards.size() == 1)
        return "单张";
    if (cards.size() == 2) {
        if (cards[0] == cards[1])
            return "一对";
        if (cards[0] == 17 && cards[1] == 16)
            return "王炸"; // "火箭"
    }
    if (cards.size() == 3)
        if (allEqual(cards))
            return "三带";
    if (cards.size() == 4) {
        if (allEqual(cards))
            return "炸弹";
        if (isTripletWithOne(cards))
            return "三带"; // 三带一
    }
    if (cards.size() == 5) {
        if (isTripletWithPair(cards))
            return "三带"; // 三带二
        if (isContinuousSingle(cards))
            return "顺子";
    }
    if (isQuadWithPair(cards))
        return "四带二";
    if (isContinuousSingle(cards))
        return "顺子";
    if (isContinuousPair(cards))
        return "连对";
    if (isAirplane(cards))
        return "飞机";
    return "无效";
}

bool CardSet::isComparable(const CardSet& rhs) const
{
    QString type = getType(), rhsType = rhs.getType();
    if (type == "无效" || rhsType == "无效")
        return false;
    if (type == "王炸" || type == "炸弹" || rhsType == "王炸" || rhsType == "炸弹")
        return true;
    return type == rhsType && cards.size() == rhs.cards.size();
}

bool CardSet::operator > (const CardSet& rhs) const
{
    QString type = getType(), rhsType = rhs.getType();
    if (type == "王炸")
        return true;
    if (rhsType == "王炸")
        return false;
    if (type == "炸弹") {
        if (rhsType == "炸弹")
            return cards[0] > rhs.cards[0];
        else
            return true;
    }
    if (rhsType == "炸弹")
        return false;
    if (type == "顺子" || type == "连对" || type == "一对" || type == "单张")
        return cards[0] > rhs.cards[0];
    if (type == "三带" || type == "飞机")
        return getValue(cards, 3) > getValue(rhs.cards, 3);
    if (type == "四带二")
        return getValue(cards, 4) > getValue(rhs.cards, 4);
    return false;
}
